/****************************************************************************
 * Copyright (c) 2009 Remy Chi Jian Suen and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * Contributors:
 *     Remy Chi Jian Suen - initial API and implementation
 *
 * SPDX-License-Identifier: EPL-2.0
 *****************************************************************************/
package org.eclipse.ecf.provider.datashare.nio;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

import org.eclipse.ecf.core.identity.ID;

final class Util {

	static final String PLUGIN_ID = "org.eclipse.ecf.provider.datashare.nio"; //$NON-NLS-1$

	static void closeChannel(Channel channel) {
		try {
			channel.close();
		} catch (IOException e) {
			// ignored
		}
	}

	static byte[] serialize(ID id) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(baos);
		oos.writeObject(id);
		oos.flush();
		return baos.toByteArray();
	}

	/**
	 * Reads messages from the channel and returns the data, if any.
	 * 
	 * @param channel
	 *            the channel to read the messages from
	 * @param buffer
	 *            the buffer to use for reading the data into
	 * @return the data from the channel and information about the channel, will
	 *         not be <code>null</code>
	 * @throws IOException
	 */
	static ChannelData read(ReadableByteChannel channel, ByteBuffer buffer)
			throws IOException {
		// check how much data has been read
		int read = channel.read(buffer);
		if (read == -1) {
			return new ChannelData(null, false);
		} else if (read == 0) {
			return new ChannelData(null, true);
		}

		// the read data
		byte[] message = new byte[read];

		// retrieve the contents from the buffer
		buffer.flip();
		buffer.get(message, 0, read);
		buffer.clear();

		// try reading more data
		read = channel.read(buffer);

		while (read > 0) {
			// create a temporary array, copy what's been read so far
			byte[] temp = new byte[read + message.length];
			System.arraycopy(message, 0, temp, 0, message.length);

			// retrieve the contents from the buffer
			buffer.flip();
			buffer.get(temp, message.length, read);
			buffer.clear();

			// assign the temporary array back to the primary
			message = temp;

			// try reading more data
			read = channel.read(buffer);
		}
		// if we didn't read zero bytes that'd imply we've reached end-of-stream
		return new ChannelData(message, read == 0);
	}

	static void write(WritableByteChannel channel, ByteBuffer buffer,
			byte[] data) throws IOException {
		buffer.clear();

		int remaining = data.length;
		int limit = buffer.limit();
		if (remaining < limit) {
			buffer.put(data);
			buffer.flip();
			channel.write(buffer);
			buffer.clear();
			return;
		}

		int offset = 0;
		while (remaining > limit) {
			buffer.put(data, offset, limit);
			buffer.flip();
			channel.write(buffer);
			buffer.clear();

			offset += limit;
			remaining -= limit;
		}

		buffer.put(data, offset, remaining);
		buffer.flip();
		channel.write(buffer);
		buffer.clear();
	}

}
